import { Axios, AxiosRequestConfig } from 'axios';
import settle, { FitUniappResponse } from './libs/settle';
import buildURL from './libs/buildURL';
import { HTTP_CONFIG } from '@/config/index';

// eslint-disable-next-line no-undef
export type CompleteResponseType = UniApp.RequestSuccessCallbackResult & UniApp.GeneralCallbackResult;

/**
 * 请求底层方法区分枚举
 */
export enum ADAPTER_TYPE {
  /**
   * 切换底层为uni.request
   */
  REQUEST = 'request',
  /**
   * 切换底层为uni.upload
   */
  UPLOAD = 'upload'
}

/**
 * 通用complete方法
 * @param response 响应体
 * @param resolve promise成功回调
 * @param reject promise失败回调
 * @param config axios的请求配置
 */
export function completeMerge(
  response: CompleteResponseType,
  resolve: Function,
  reject: Function,
  config: AxiosRequestConfig
) {
  const newResponse: FitUniappResponse = {
    data: (response as CompleteResponseType).data,
    status: (response as CompleteResponseType).statusCode,
    errMsg: (response as CompleteResponseType).errMsg,
    header: (response as CompleteResponseType).header,
    config
  };
  settle(resolve, reject, newResponse);
}

interface UploadFileOptionFiles {
  /**
   * multipart 提交时，表单的项目名，默认为 file，如果 name 不填或填的值相同，可能导致服务端读取文件时只能读取到一个文件。
   */
  name?: string;
  /**
   * 要上传的文件对象
   */
  file?: File;
  /**
   * 要上传文件资源的路径
   */
  url?: string;
}

export interface AxiosRequestConfigMergeDataNotFormData {
  // 如果出现官方更新，需要维护
  /**
   * 文件类型，image/video/audio，仅支付宝小程序，且必填。
   * - image: 图像
   * - video: 视频
   * - audio: 音频
   */
  fileType?: 'image' | 'video' | 'audio';
  /**
   * 要上传的文件对象
   */
  file?: File;
  /**
   * 要上传文件资源的路径
   */
  filePath?: string;
  /**
   * 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
   */
  name?: string;
  /**
   * 需要上传的文件列表。
   */
  files?: UploadFileOptionFiles[];
}

export interface AxiosRequestConfigMergeData extends AxiosRequestConfigMergeDataNotFormData {
  /**
   * HTTP 请求中其他额外的 form data
   */
  formData?: any;
}

/**
 * 兼容uni.uploadFile方法
 */
export interface AxiosRequestConfigMerge extends AxiosRequestConfig {
  /**
   * upload方法相关参数
   */
  data: AxiosRequestConfigMergeData;
}

export default function fitUniapp(axios: Axios) {
  // 自定义适配器
  axios.defaults.adapter = (config: AxiosRequestConfig) => {
    return new Promise((resolve, reject) => {
      if (config.headers?.$requestType === ADAPTER_TYPE.UPLOAD) {
        delete config.headers.$requestType;
        delete config.headers['Content-Type'];
        const uploadConfig = config as AxiosRequestConfigMerge;
        uni.uploadFile({
          /**
           * 开发者服务器 url
           */
          url: HTTP_CONFIG.UPLOAD_HOST + buildURL(config?.url, config.params, config.paramsSerializer),
          /**
           * 文件类型，image/video/audio，仅支付宝小程序，且必填。'image' | 'video' | 'audio';
           * - image: 图像
           * - video: 视频
           * - audio: 音频
           */
          fileType: uploadConfig.data.fileType,
          /**
           * 要上传的文件对象
           */
          file: uploadConfig.data.file,
          /**
           * 要上传文件资源的路径
           */
          filePath: uploadConfig.data.filePath,
          /**
           * 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
           */
          name: uploadConfig.data.name,
          /**
           * 需要上传的文件列表。仅h5支持
           */
          files: uploadConfig.data.files,
          /**
           * HTTP 请求 Header, header 中不能设置 Referer
           */
          header: uploadConfig.headers,
          /**
           * HTTP 请求中其他额外的 form data
           */
          formData: uploadConfig.data.formData,
          /**
           * 超时时间，单位 ms
           */
          timeout: uploadConfig.timeout,
          /**
           * 成功返回的回调函数
           */
          success() {},
          /**
           * 失败的回调函数
           */
          fail() {},
          /**
           * 结束的回调函数（调用成功、失败都会执行）
           */
          complete(response) {
            completeMerge(response as CompleteResponseType, resolve, reject, uploadConfig);
          }
        });
        return;
      }
      uni.request({
        // eslint-disable-next-line no-undef
        method: (config?.method?.toUpperCase() as UniApp.RequestOptions['method']) || 'GET',
        url: config.baseURL + buildURL(config?.url, config.params, config.paramsSerializer),
        header: config.headers,
        data: config.data,
        dataType: 'json',
        responseType: config.responseType,
        sslVerify: true,
        timeout: config.timeout,
        complete(response) {
          completeMerge(response as CompleteResponseType, resolve, reject, config);
        }
      });
    });
  };
}
